<?php
/**
 * Ecga数据表业务模型抽象
 *
 * PHP Version 5
 *
 * @package    Ecga
 * @subpackage Models
 * @author     v.k <none@none.com>
 * @copyright  2010 Xingchangxinda Inc.
 * @version    SVN:$Id$
 */
namespace Project\Apps\Ecga\Models\Table;

use \Exception, \DateTime, \DateInterval;
use \Project\Apps\Ecga\Library\Visitor;
use \Project\Apps\Ecga\Library\WmsClient;
use \Framework\System\Helper\HttpRequest;

/**
 * Ecga数据表业务模型抽象
 *
 * @package   Ecga
 * @author    v.k <none@none.com>
 * @copyright 2010 Xingchangxinda Inc.
 * @version   Release: v0.1
 */
abstract class EcgaTable
{
    /**
     * 筛选器
     */
    protected $filters = array();

    /**
     * 表头组
     */
    public $headerGroups = array();

    /**
     * 表头
     */
    public $headers = array();

    /**
     * 排序字段
     */
    public $sortField = null;
    
    const SRP_MOP = 0.92;// 定义常量 计算mop
    /**
     * 默认排序字段
     */
    public $defaultSort = null;

    /**
     * 排序方法
     */
    public $sortMethod = 'desc';

    /**
     * 数据行
     */
    public $rows = array();

    /**
     * 图表数据
     */
    public $chartData = array();
    
    /**
     * 格式化对象集
     */
    protected $formaters = array();

    /**
     * 表名称
     */
    public $name = '';

    /**
     * 表说明
     */
    public $desc = '';

    /**
     * 表模版
     */
    public $template = 'default';

    /**
     * 构造
     *
     * @return void
     */
    public function __construct()
    {
        // 初始化过滤器
        $this->initFilters();
    }

    /**
     * 初始化筛选器
     *
     * @return void
     */
    protected function initFilters()
    {
        // 设置默认筛选器集
        $this->filters = Filter::createFromMultiArray(
            array(
                'drs' => array(
                    'type' => Filter::FILTER_TYPE_DATERANGE,
                    'default' => date('Y-m-d', strtotime('-1 day')) . ' to ' . date('Y-m-d', strtotime('-1 day')),
                    'caption' => '日期区间',
                    //'validator' => new DateTimeValidator
                ),
                /*
                'startDate' => array(
                    'type' => Filter::FILTER_TYPE_DATE,
                    'default' => date('Y-m-01', strtotime('-1 day')),
                    'caption' => '开始日期',
                    'validator' => new DateTimeValidator
                ),

                'endDate' => array(
                    'type' => Filter::FILTER_TYPE_DATE,
                    'default' => date('Y-m-d', strtotime('-1 day')),
                    'caption' => '结束日期',
                    'validator' => new DateTimeValidator
                ),
                */

                'projectCateId' => array(
                    'type' => Filter::FILTER_TYPE_SELECT,
                    'default' => 0,
                    'bind' => model('ProjectCates')->getCates(),
                    'caption' => '类目'
                ),

                'projectTypeId' => array(
                    'type' => Filter::FILTER_TYPE_SELECT,
                    'default' => 0,
                    'bind' => model('ProjectTypes')->getTypes(),
                    'caption' => '性质'
                ),

                'projectBrandId' => array(
                    'type' => Filter::FILTER_TYPE_SELECT,
                    'default' => 0,
                    'bind' => model('ProjectBrands')->getBrands(),
                    'caption' => '品牌'
                )
            )
        );
    }

    /**
     * 取数据表头
     *
     * @return array
     */
    public function getHeaderGroups()
    {
        return $this->headerGroups;
    }

    /**
     * 取筛选器
     *
     * @return array
     */
    public function getFilters()
    {
        return $this->filters;
    }

    /**
     * 设置筛选器值
     *
     * @param string $name  筛选器名称
     * @param mixed  $value 筛选器值
     *
     * @return EcgaTable
     */
    public function setFilterValue($name, $value)
    {
        if (!isset($this->filters[$name])) {
            return ;
        }

        $filter = $this->filters[$name];
        if ($filter->validator) {
            $value = $filter->validator->validate($value) ?
                $value : $filter->default;
        }

        $filter->value = $value;

        return $this;
    }

    /**
     * 设置表数据行格式化对象
     *
     * @param IRowFormater $formater 格式化对象
     * @param string       $table    表名称
     *
     * @return EcgaTable
     */
    public function setRowFormater($formater, $table)
    {
        if (!is_object($formater)) {
            $formater = RowFormater::create($formater, $table);
        }
        $this->formaters[get_class($formater)] = $formater;
        return $this;
    }


    /**
     * 创建数据行
     *
     * @return array
     */
    protected function createDataRow()
    {
        static $tplRow = null;

        if (null == $tplRow) {

            $tplRow = array_fill_keys(
                array_keys($this->headers), null
            );

        }

        return $tplRow;
    }

    /**
     * 计算环比增长率
     *
     * @param float $current 当前数
     * @param float $pre     前期数
     *
     * @return float
     */
    protected function calcRelativeRatio($current, $pre)
    {
        return $pre == 0 ? null : round(($current - $pre) / $pre, 4);
    }

    /**
     * 求分数,分母为0时则返回NULL
     *
     * @param float $n1        分子
     * @param float $n2        分母
     * @param float $precision 要保留的小数(四舍五入)
     *
     * @return float
     */
    protected function calcDiv($n1, $n2, $precision = 2)
    {
        return $n2 == 0 ? null : round($n1 / $n2, $precision);
    }

    /**
     * 计算DOS
     * 
     * @param mixed $n1 期末库存（数量、成本）
     * @param mixed $n2 15天的出库（数量、金额）
     * 
     * @return mixed
     */
    protected function calcDos($n1, $n2) 
    {
        if (0 == $n2) {
            return null;
        } else {
            return ceil(($n1 / $n2) * 15);
        }
    }

    /**
     * 增加或减少日期天数得到新日期
     *
     * @param string $date 要计算的基准日期
     * @param int    $days 天数，整数为增加，负数为减少
     *
     * @return string
     */
    private function _addDays($date, $days)
    {
         $days = strval($days);
         $dateTime = new DateTime($date);
         $dateInterval = DateInterval::createFromDateString("$days day");
         return date_add($dateTime, $dateInterval)->format('Y-m-d');
    }

    /**
     * 根据日期取环比(RelativeRatio)的计算日期
     *
     * @return array 开始日期和结束日期
     */
    protected function getRRDate()
    {
        list($startDate, $endDate) = explode(' to ', $this->filters['drs']->value);
        // 取时间差计算环比
        $dateTimeEnd = new DateTime($startDate);
        $dateTimeStart = new DateTime($endDate);
        $timeSpan = $dateTimeStart->diff($dateTimeEnd);

        $days = $timeSpan->days + 1;

        $s = array(
            'startDate' => $this->_addDays(
                $startDate,
                -$days
            ),
            'endDate' => $this->_addDays(
                $startDate,
                -1
            )
        );

        return $s;
    }

    /**
     * 获取所有项目
     *
     * @return array
     */
    protected function getProjects()
    {
        static $projects = null;
        if (null === $projects) {
            $projects = model('Projects')->getProjects();
            $passport = Visitor::getInstance()->getPassport();
            $tableName = $this->getTableClassName();
            foreach ($projects as $key => $value) {
                //去除没有查看权限的项目
                if (!$passport->hasProjectPriviage($value['id'], $tableName)) {
                    unset($projects[$key]);
                }
            }
        }
        return $projects;
    }
    
    /**
     * 获取当前类名 去掉命名空间
     * 
     * @return string
     */
    protected function getTableClassName()
    {
        $ns = get_called_class();
        return substr($ns, strrpos($ns, '\\') + 1);
    }
    
    /**
     * 获取所有站点
     *
	 * @param int $projectId 项目ID
	 *
     * @return array
     */
    protected function getSites($projectId = 0)
    {
        static $sites = null;
        if (null === $sites) {
            $sites = model('Sites')->getSites($projectId);
        }
        return $sites;
    }
    
    /**
     * 将数组转换成options
     * 
     * @param array  $data  数据
     * @param string $key   键
     * @param string $value 值
     * 
     * @return array
     * @author FarmerLi <lixu_01@126.com>
     */
    public function arrayToOptions($data, $key, $value) 
    {
        $return = array();
        if (!is_array($data)) {
            return $return;
        }
        foreach ($data as $k => $v) {
            $return[$v[$key]] = $v[$value];
        }
        return $return;
    }

    /**
     * 取数据行总数
     *
     * @return int
     */
    public function getRowCount()
    {
        return count($this->rows);
    }

    /**
     * 生成数据表
     *
     * @return void
     */
    public function generate()
    {
        // 设置表头组
        $this->setHeaderGroups();
        
        // 萃取表头
        foreach ($this->headerGroups as $header) {
            $this->headers += $header;
        }
        //合成完整表头
        $this->createFillHeaders();
        // 填充表数据
        $this->fillData();
        //对数据进行排序
        $this->sortData();
        
        //生成图表数据
        $this->createChartData();
        // 格式表数据
        if (is_array($this->rows)) {
            foreach ($this->rows as $key => $row) {
                foreach ($this->formaters as $formater) {
                    $row = $formater->format($row);
                }
                $this->rows[$key] = $row;
            }
        }
    }
    
    /**
     * 创建图表所需数据
     * 
     * @return void
     */
    abstract protected function createChartData();

    /**
     * 对表格数据进行排序
     * 冒泡排序
     *
     * @return void
     */
    protected function sortData()
    {
        $count = count($this->rows);
        if ($this->sortField == '' && $this->defaultSort != '') {
            $this->sortField = $this->defaultSort;
        }
        if ('' == $this->sortField || $count <= 1) {
            return ;
        }
        $sortField = $this->sortField;
        if (!array_key_exists($sortField, $this->headers)) {
            return ;
        }
        //默认由大到小
        $sort = function ($a, $b) use ($sortField) {
            if (stripos($sortField, 'dos')) {
                if (is_null($a[$sortField])) {
                    return -1;
                } elseif (is_null($b[$sortField])) {
                    return 1;
                }
            } else {
                if (is_null($a[$sortField])) {
                    return 1;
                } elseif (is_null($b[$sortField])) {
                    return -1;
                }
            }
            //return ($b[$sortField] - $a[$sortField]) > 0 ? 1 : -1;
            return $b[$sortField] > $a[$sortField] ? 1 : -1;
        };
        usort($this->rows, $sort);
        //如果设置的排序方法为由小到大, 只要将已排序数组反序即可
        if ('asc' === $this->sortMethod) {
            $this->rows = array_reverse($this->rows);
        }
    }

    /**
     * 创建完整的表头
     *
     * @return void
     */
    protected function createFillHeaders()
    {
        $request = HttpRequest::getInstance();
        $query = array();
        foreach ($this->filters as $key => $filter) {
            $query[] = sprintf("%s=%s", $key, urlencode($filter->value));
        }
        //当前有存在排序字段, 则将此字段的链接地址设置反序
        if (array_key_exists($this->sortField, $this->headers)) {
            $this->headers[$this->sortField]['sortMethod'] = $this->sortMethod == 'desc' ? 'asc' : 'desc';
        }
        $icon = array(
            'desc' => '&uarr;',
            'asc' => '&darr;'
        );
        foreach ($this->headers as $key => $header) {
            $header['fillName'] = $header['name'];
            //如果已自定义了链接, 则禁用字段排序
            if (isset($header['link']) && null !== $header['link']) {
                $header['fillName'] = sprintf(
                    "<a href=\"%s\" class=\"sort-link\">%s</a>", 
                    $header['link'], 
                    $header['fillName']
                );
                $header['allowSort'] = false;
            }
            
            if (isset($header['allowSort']) && true === $header['allowSort']) {
                
                if (!isset($header['sortMethod'])) {
                    $header['sortMethod'] = 'desc';
                }
                $tmpQuery = $query;
                $tmpQuery[] = "sortfield=$key";
                $tmpQuery[] = "sortmethod={$header['sortMethod']}";
                $strQuery = implode('&', $tmpQuery);
                $uri = sprintf('%s?%s', $request->scriptName, $strQuery);
                $currIcon = $this->sortField === $key ? $icon[$header['sortMethod']] : '';
                $header['fillName'] = sprintf(
                    "<a href=\"%s\" class=\"sort-link\">%s%s</a>",
                    $uri,
                    $header['fillName'],
                    $currIcon
                );
            }
            
            if (isset($header['class']) && '' !== $header['class']) {
                $header['fillName'] = sprintf("<span class=\"%s\">%s</span>", $header['class'], $header['fillName']);
            }
            $this->headers[$key] = $header;
        }
    }

    /**
     * 创建表对象
     *
     * @param string $table 表名称
     *
     * @return EcgaTable
     */
    public static function create($table)
    {
        $klass = sprintf('%s\\%s', __NAMESPACE__, ucfirst($table));
        try {
            return class_exists($klass) ? new $klass : null;
        } catch (Exception $ex) {
            var_dump($ex);
            return null;
        }
    }

    /**
     * 设置数据表头
     *
     * @return void
     */
    protected abstract function setHeaderGroups();

    /**
     * 填充数据表数据
     *
     * @return void
     */
    protected abstract function fillData();
    
	/**
	 * 获得全部 model
	 *
	 * @author Wanggeng <wanggeng@ec3s.com>
	 *
	 * @return array 
	 */
	public function getModels()
	{
		$result = model('Skus')->getModels();
		foreach ($result as $value) {
            $return[] = array(
                'modelId' => urlencode($value['model']),  //model 中含有 +号被url转义 再次加密下
                'modelName' => $value['model']
            );
        }
		return $return;
	}
}

/**
 * Ecga数据表数据筛选器
 *
 * @package   Ecga
 * @author    v.k <none@none.com>
 * @copyright 2010 Xingchangxinda Inc.
 * @version   Release: v0.1
 */
class Filter
{
    /**
     * 日期型筛选器
     */
    const FILTER_TYPE_DATE = 1001;

    /**
     * 选择型筛选器
     */
    const FILTER_TYPE_SELECT = 1002;
    const FILTER_TYPE_RADIO = 1005;

    /**
     * 文本型筛选器
     */
    const FILTER_TYPE_TEXT = 1003;
    const FILTER_TYPE_DATERANGE = 1004;
    const FILTER_TYPE_TIPTEXT = 1006;

    /**
     * 筛选器属性集
     *
     * @var array
     */
    protected $properties = array(
        'name'      => '',  // 筛选器名称
        'value'     => '',  // 筛选器值
        'caption'   => '',  // 筛选器标题
        'default'   => '',  // 默认值
        'size'      => '',  // 筛选器大小
        'type'      => '',  // 类型
        'validator' => '',  // 筛选器值验证器
        'bind'      => '',  // 选择型筛选器的选择项绑定
        'readOnly'  => '',
        'ajax'      => ''   // TipText查询路径
    );

    /**
     * 构造
     *
     * @param array $properties 筛选器属性集合
     */
    public function __construct($properties)
    {
        foreach ($properties as $name => $value) {
            $this->checkProperty($name);
            $this->properties[$name] = $value;
        }
    }

    /**
     * 检查属性合法性
     *
     * @param string $name 筛选器属性名
     *
     * @return void
     * @throws Exception
     */
    protected function checkProperty($name)
    {
        if (!isset($this->properties[$name])) {
            throw new Exception("property $name not exists in this filter");
        }
    }

    /**
     * 类属性映射到属性集
     *
     * @param array $name 属性名
     *
     * @return mixed
     */
    public function __get($name)
    {
        $this->checkProperty($name);

        if ($name == 'value' && empty($this->properties[$name])) {
            $this->properties['value'] = $this->properties['default'];
        }

        return $this->properties[$name];
    }

    /**
     * magic call
     *
     * @param string $name called name
     * @param array  $args args
     *
     * @return mixed
     */
    public function __call($name, $args)
    {
        static $ref = null;
        if (null === $ref) {
            $ref = new \ReflectionClass(get_class());
        }
        $type = strtoupper(str_replace('is', '', $name));
        $constant = $ref->getConstant(
            sprintf('FILTER_TYPE_%s', $type)
        );
        if ($constant != null) {
            return $this->type == $constant;
        }
        return false;
    }

    /**
     * 设置属性
     *
     * @param string $name  属性名
     * @param mixed  $value 属性值
     *
     * @return void
     */
    public function __set($name, $value)
    {
        $this->checkProperty($name);
        $this->properties[$name] = $value;
    }

    /**
     * 从多维数组中创建筛选器集合
     *
     * @param array $array 筛选器多维数组
     *
     * @return array(this)
     */
    public static function createFromMultiArray($array)
    {
        $result = array();

        foreach ($array as $name => $value) {
            $value['name'] = $name;
            $result[$name] = self::createFromArray($value);
        }
        return $result;
    }

    /**
     * 从数组中创建筛选器
     *
     * @param array $array 筛选器属性集
     *
     * @return EcgaTable
     */
    public static function createFromArray($array)
    {
        return new self($array);
    }
}

/**
 * 筛选器数据验证接口
 *
 * @package   Ecga
 * @author    v.k <none@none.com>
 * @copyright 2010 Xingchangxinda Inc.
 * @version   Release: v0.1
 */
interface IFilterValidator
{
    /**
     * 验证
     *
     * @param mixed $value 值
     *
     * @return bool
     */
    public function validate($value);
}

/**
 * 日期验证
 *
 * @package   Ecga
 * @author    v.k <none@none.com>
 * @copyright 2010 Xingchangxinda Inc.
 * @version   Release: v0.1
 */
class DateTimeValidator implements IFilterValidator
{
    /**
     * 验证
     *
     * @param mixed $value 值
     *
     * @return bool
     */
    public function validate($value)
    {
        try {
            new DateTime($value);
            return true;
        } catch (Exception $ex) {
            return false;
        }
    }
}
